﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;

namespace TileMapUtils
{
    [ExecuteInEditMode]
    public class TileMapDataExporter : MonoBehaviour
{

    [Serializable]
    public class IndexSizeInfo
    {
        public int BuildingID = 0;
        public int Index = 0;
        public int Size = 0;
    }
    
    // key : 建筑ID，value：对应索引
    private Dictionary<int,int> TileIDDic = new Dictionary<int, int>()
    {
        {61201,1},
        {61202,2},
        {61203,3},
    };
    
    private Dictionary<int,int> TileSizeDic = new Dictionary<int, int>()
    {
        {61201,1},
        {61202,1},
        {61203,1},
    };
    
    
    private Dictionary<int,int> RoadIDDic = new Dictionary<int, int>()
    {
        {61101,1},
        {61145,2},
    };
    
    private Dictionary<int,int> RoadSizeDic = new Dictionary<int, int>()
    {
        {61101,2},
        {61145,1},
    };
    
    
    private const string configPath = "/../Lua/modules/city/config/";
    //public string TileConfigFileName = "TileConfig";
    //public string RoadConfigFileName = "RoadConfig";

    public List<IndexSizeInfo> TileConfig;
    public List<IndexSizeInfo> RoadConfig;
    
    
    public void Exporter()
    {
#if UNITY_EDITOR

        
        TileIDDic = new Dictionary<int, int>();
        TileSizeDic = new Dictionary<int, int>();

        foreach (var tileInfo in TileConfig)
        {
            TileIDDic[tileInfo.BuildingID] = tileInfo.Index;
            TileSizeDic[tileInfo.BuildingID] = tileInfo.Size;
        }
        
        RoadIDDic = new Dictionary<int, int>();
        RoadSizeDic = new Dictionary<int, int>();

        foreach (var roadInfo in RoadConfig)
        {
            RoadIDDic[roadInfo.BuildingID] = roadInfo.Index;
            RoadSizeDic[roadInfo.BuildingID] = roadInfo.Size;
        }
        
        
        var readPath = Path.Combine(Application.dataPath, "Arts/TileMap/mapInput/MapData.json");
        var outputPath = Path.Combine(Application.dataPath, "Arts/TileMap/mapOut/");

        var str = File.ReadAllText(readPath);
        var exportObj = JsonConvert.DeserializeObject<TileMapExport>(str);

        var serverData = new ServerCfg();
        serverData.width = exportObj.height;
        serverData.height = exportObj.width;
        serverData.data = new long[serverData.width * serverData.height];

        var index2FogSvr = new Dictionary<int, int>();

        var clienData = new ClientCfg();
        clienData.width = exportObj.height;
        clienData.height = exportObj.width;
        clienData.groundLayer = new int[clienData.width * clienData.height];
        clienData.roadLayer = new int[clienData.width * clienData.height];
        clienData.terrainLayer = new int[clienData.width * clienData.height];
        clienData.areaLayer = new int[clienData.width * clienData.height];
        clienData.buildingLayer = new int[clienData.width * clienData.height];
        clienData.fogLayer = new int[clienData.width * clienData.height];

        var tiles = exportObj.tilesets[0].tiles;
        var valueDict = new Dictionary<string, Dictionary<int, int>>();
        foreach (var tile in tiles)
        {
            foreach (var property in tile.properties)
            {
                Dictionary<int, int> t;
                if (!valueDict.TryGetValue(property.name, out t))
                {
                    t = new Dictionary<int, int>();
                    valueDict.Add(property.name, t);
                }

                t.Add(tile.id + 1, property.value);
            }
        }

        foreach (var layer in exportObj.layers)
        {
            Dictionary<int, int> id2Value;
            if (!valueDict.TryGetValue(layer.name, out id2Value))
                continue;

            if (layer.name == "GroundLayer")
            {
                for (var i = 0; i < layer.data.Length; i++)
                {
                    int value;
                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.width);
                        serverData.data[index] |= (long) value;
                        clienData.groundLayer[index] = value;
                    }
                }
            }
            else if (layer.name == "RoadLayer")
            {
                for (var i = 0; i < layer.data.Length; i++)
                {
                    int value;
                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.width);
                        clienData.roadLayer[index] = value;
                    }
                }
            }
            else if (layer.name == "AreaLayer")
            {
                for (var i = 0; i < layer.data.Length; i++)
                {
                    int value;
                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.width);
                        serverData.data[index] |= (long) value << 1;
                        clienData.areaLayer[index] = value;
                    }
                }
            }
            else if (layer.name == "FogLayer")
            {
                for (var i = 0; i < layer.data.Length; i++)
                {
                    int value;
                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.width);
                        serverData.data[index] |= (long) value << 9;
                        clienData.fogLayer[index] = value;
                    }
                }
            }
            else if (layer.name == "BuildingLayer")
            {
                Dictionary<int, int> id2UnlockFog = null;
                valueDict.TryGetValue("UnLockFog", out id2UnlockFog);

                for (var i = 0; i < layer.data.Length; i++)
                {
                    var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.width);
                    int value;
                    if (id2Value != null && id2UnlockFog.TryGetValue(layer.data[i], out value))
                        index2FogSvr[index] = value;

                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        if (!RoadIDDic.ContainsKey(value))
                        {
                            serverData.data[index] |= (long) value << 17;
                            clienData.buildingLayer[index] = value;
                        }
                        else
                        {
                            UnityEngine.Debug.LogError("道路配置在了建筑层：" + value);   
                        }
                    }
                }
            }
            else if (layer.name == "TerrainLayer")
            {
                for (var i = 0; i < layer.data.Length; i++)
                {
                    int value;
                    if (id2Value.TryGetValue(layer.data[i], out value))
                    {
                        var index = TileMapIndex2GameIndex(i, exportObj.width, exportObj.height);
                        //serverData.data[index] |= (long) value << 33;
                        clienData.terrainLayer[index] = value;
                    }
                }
            }
        }

        //---------------------------------------------------------------------- 得到道路层和地表层的索引信息
        // 道路层
        int[,] roadCollection = new int[exportObj.width,exportObj.height];
        for (int i = 0; i < clienData.roadLayer.Length; i++)
        {
            int bId = clienData.roadLayer[i];

            if (bId == 0)
            {
                continue;
            }

            if (!RoadIDDic.ContainsKey(bId))
            {
                UnityEngine.Debug.LogError("不包含此id的配置："+bId);
                continue;
            }
            
            int iX = i % exportObj.height;    
            int iY = i / exportObj.height;        // 当前索引


            int dirID = 0;

            int size = RoadSizeDic[bId];
            
            
            if (iX-size >= 0)
            {
                // 左边
                if (clienData.roadLayer[iY * exportObj.height + iX-size] == bId)
                {
                    dirID += 2;
                }
                
            }

            if (iX + size <exportObj.width)
            {
                // 右边
                if (clienData.roadLayer[iY * exportObj.height + iX+size] == bId)
                {
                    dirID += 8;
                }

            }


            if (iY - size >= 0)
            {
                // 下面
                if (clienData.roadLayer[(iY-size) *exportObj.height +iX] == bId)
                {
                    dirID += 4;
                }

                
            }

            if (iY + size < exportObj.height)
            {
                // 上面
                if (clienData.roadLayer[(iY+size) *exportObj.height +iX] == bId)
                {
                    dirID += 1;
                }

            }
            
            dirID = (dirID + 1) + (RoadIDDic[bId] - 1) * 16;

            roadCollection[iY, iX] = dirID;
            
            //serverData.data[iY * exportObj.height + iX] |= (long) dirID << 33;
        }

        
        
        
        // 地表层
        int[,] terrainCollection = new int[exportObj.width,exportObj.height];

        for (int i = 0; i < clienData.terrainLayer.Length; i++)
        {
            int bId = clienData.terrainLayer[i];

            if (bId == 0)
            {
                continue;
            }

            if (!TileIDDic.ContainsKey(bId))
            {
                //UnityEngine.Debug.LogError("不包含此id的配置："+bId);
                continue;
            }
            
            int iX = i % exportObj.height;    
            int iY = i / exportObj.height;        // 当前索引


            int dirID = 0;

            int size = TileSizeDic[bId];
            
            
            if (iX-size >= 0)
            {
                // 左边
                if (clienData.terrainLayer[iY * exportObj.height + iX-size] == bId)
                {
                    dirID += 2;
                }
                
            }

            if (iX + size <exportObj.width)
            {
                // 右边
                if (clienData.terrainLayer[iY * exportObj.height + iX+size] == bId)
                {
                    dirID += 8;
                }

            }


            if (iY - size >= 0)
            {
                // 下面
                if (clienData.terrainLayer[(iY-size) *exportObj.height +iX] == bId)
                {
                    dirID += 4;
                }

                
            }

            if (iY + size < exportObj.height)
            {
                // 上面
                if (clienData.terrainLayer[(iY+size) *exportObj.height +iX] == bId)
                {
                    dirID += 1;
                }

            }
            
            dirID = (dirID + 1) + (TileIDDic[bId] - 1) * 16;

            terrainCollection[iY, iX] = dirID;
            
            //serverData.data[iY * exportObj.height + iX] |= (long) dirID << 41;
        }
        
        for (int i = 0; i < roadCollection.GetLength(0); i++)
        {
            for (int j = 0; j < roadCollection.GetLength(1); j++)
            {
                serverData.data[i*exportObj.height + j] |= ((long)roadCollection[i,j] << 33);
            }
        }
        
        for (int i = 0; i < terrainCollection.GetLength(0); i++)
        {
            for (int j = 0; j < terrainCollection.GetLength(1); j++)
            {
                serverData.data[i*exportObj.height + j]|= ((long)terrainCollection[i,j]<<41);
            }
        }

        

        var json = JsonConvert.SerializeObject(serverData);
        File.WriteAllText(outputPath + "new_city_landform.json", json);

        json = JsonConvert.SerializeObject(index2FogSvr);
        File.WriteAllText(outputPath + "ServerIndex2UnlockFog.json", json);

        json = JsonConvert.SerializeObject(clienData);
        File.WriteAllText(outputPath + "ClientData.json", json);

        if (EditorUtility.DisplayDialog("","导出成功，是否打开文件夹","确认","取消"))
        {
            Process.Start(outputPath);
        }
        
      /*  
        string path = Application.dataPath + configPath;

        string filePath = path + RoadConfigFileName + ".lua";
        if (!Directory.Exists (path)) {
            Directory.CreateDirectory (path);
        }
        StringBuilder sb = new StringBuilder ();
        sb.AppendLine ("return {");


        for (int i = 0; i < roadCollection.GetLength(0); i++)
        {
            for (int j = 0; j < roadCollection.GetLength(1); j++)
            {
                sb.AppendLine (string.Format ("[{0}] = {1},",  i*exportObj.height + j + 1,roadCollection[i,j]));
            }
        }


        sb.AppendLine ("}");

        StreamWriter streamWriter = new StreamWriter (filePath);
        streamWriter.Write (sb.ToString ());
        streamWriter.Flush ();
        streamWriter.Close ();

        AssetDatabase.Refresh ();
        Debug.Log ("导出数据成功：" + filePath);
        
        
        // --------------------------------------------------------------------------------------------------
        
        
        string filePath2 = path + TileConfigFileName + ".lua";
        if (!Directory.Exists (path)) {
            Directory.CreateDirectory (path);
        }
        StringBuilder sb2 = new StringBuilder ();
        sb2.AppendLine ("return {");


        for (int i = 0; i < terrainCollection.GetLength(0); i++)
        {
            for (int j = 0; j < terrainCollection.GetLength(1); j++)
            {
                sb2.AppendLine (string.Format ("[{0}] = {1},",  i*exportObj.height + j + 1,terrainCollection[i,j]));
            }
        }


        sb2.AppendLine ("}");

        StreamWriter streamWriter2 = new StreamWriter (filePath2);
        streamWriter2.Write (sb2.ToString ());
        streamWriter2.Flush ();
        streamWriter2.Close ();
     Debug.Log ("导出数据成功：" + filePath2);
*/
        AssetDatabase.Refresh ();
                
#endif
    }

    public static int TileMapIndex2GameIndex(int tIndex, int tWidth, int tHeight)
    {
        var tY = tIndex / tWidth;
        var tX = tIndex - (tWidth * tY);
        return tX * tHeight + tY;
    }
}


public class TileMapExport
{
    public int compressionlevel;
    public object editorsettings;
    public int height;
    public bool infinite;
    public TileMapLayer[] layers;
    public int nextlayerid;
    public int nextobjectid;
    public string orientation;
    public string renderorder;
    public string tiledversion;
    public int tileheight;
    public TileMapSet[] tilesets;
    public int tilewidth;
    public string type;
    public string version;
    public int width;
}

public class TileMapLayer
{
    public int[] data;
    public int height;
    public int id;
    public string name;
    public int opacity;
    public string type;
    public bool visible;
    public int width;
    public int x;
    public int y;
}

public class TileMapSet
{
    public int columns;
    public int firstgid;
    public string image;
    public int imageheight;
    public int imagewidth;
    public int margin;
    public string name;
    public int spacing;
    public int tilecount;
    public int tileheight;
    public Tile[] tiles;
    public int tilewidth;
    public object wangsets;
}

public class Tile
{
    public int id;
    public TileProperty[] properties;
}

public class TileProperty
{
    public string name;
    public string type;
    public int value; // todo 如果拓展了参数的数据类型，需要自动匹配Type
}

public class ServerCfg
{
    public int width;
    public int height;
    public long[] data;
}

public class ClientCfg
{
    public int width;
    public int height;
    public int[] groundLayer;
    public int[] roadLayer;
    public int[] terrainLayer;
    public int[] areaLayer;
    public int[] buildingLayer;
    public int[] fogLayer;
}

}

